//**************************************************
// Description: generate protocol response 
//**************************************************

//**************************************************
// include files
//**************************************************
`include "protocol_define.v"


module protocol_response #(
    parameter IN_DATA_WIDTH     = 128   ,
    parameter OUT_ORDER_WIDTH   = 30    ,
    parameter OUT_ADDR_WIDTH    = 32    ,
    parameter OUT_DATA_WIDTH    = 32  
    ) (
    input                               clk ,
    input                               rst_n ,
    // pre-module(async FIFO read)
    output                              ACTION_rd_en ,
    input   [OUT_ORDER_WIDTH-1:0]       ACTION_rd_order ,
    input   [OUT_ADDR_WIDTH+1:0]        ACTION_rd_addr ,
    input   [OUT_DATA_WIDTH-1:0]        ACTION_rd_data ,
    input                               ACTION_rd_empty ,
    // post-module(UDP frame gen)
    input                               protocol_gen_rdy ,
    input                               protocol_start_trans ,
    output  [IN_DATA_WIDTH-1-32:0]      protocol_gen_data , // must be 12*n Bytes
    output                              protocol_gen_sav ,
    output                              protocol_gen_val ,
    output                              protocol_gen_sop ,
    output                              protocol_gen_eop ,
    output                              convert_frame_info  //read enable frame info FIFO 
) ;

    //----------------------------------------------
    // signal declare
    //----------------------------------------------
    // "empty" delay
    reg                             ACTION_rd_empty_ff1     ;
    wire                            ACTION_FIFO_is_empty    ;
    assign  ACTION_FIFO_is_empty = ACTION_rd_empty_ff1 && ACTION_rd_empty ;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            ACTION_rd_empty_ff1     <= 1'b1 ;
        end
        else begin
            ACTION_rd_empty_ff1     <= ACTION_rd_empty ;
        end
    end
    // async FIFO output signal delay(only use when "IDLE -> WAIT")
    reg                                     ACTION_rd_en_r_ff1 ;
    reg     [OUT_ORDER_WIDTH-1:0]           ACTION_rd_order_ff1 ;
    reg                                     ACTION_rd_wren_ff1  ;
    reg     [OUT_ADDR_WIDTH-1:0]            ACTION_rd_addr_ff1  ;
    reg     [OUT_DATA_WIDTH-1:0]            ACTION_rd_data_ff1  ;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            ACTION_rd_order_ff1     <= {(OUT_ORDER_WIDTH){1'b0}} ;
            ACTION_rd_wren_ff1      <= {(1){1'b0}}               ;
            ACTION_rd_addr_ff1      <= {(OUT_ADDR_WIDTH){1'b0}}  ;
            ACTION_rd_data_ff1      <= {(OUT_DATA_WIDTH){1'b0}}  ;
        end
        else if( ACTION_rd_en_r_ff1 ) begin
            ACTION_rd_order_ff1     <= ACTION_rd_order ;
            ACTION_rd_wren_ff1      <= ACTION_rd_addr[OUT_ADDR_WIDTH] ;
            ACTION_rd_addr_ff1      <= ACTION_rd_addr ;
            ACTION_rd_data_ff1      <= ACTION_rd_data ;
        end
        else begin
            ACTION_rd_order_ff1     <= ACTION_rd_order_ff1 ;
            ACTION_rd_wren_ff1      <= ACTION_rd_wren_ff1  ;
            ACTION_rd_addr_ff1      <= ACTION_rd_addr_ff1  ;
            ACTION_rd_data_ff1      <= ACTION_rd_data_ff1  ;
        end
    end


    // indicate the first time to "TRANS"
    reg                                     TRANS_first ;

    // path 2 signal
    reg                                     ACTION_has_store ;
    reg     [OUT_ORDER_WIDTH-1:0]           ACTION_stored_order ;
    reg                                     ACTION_stored_wren ;
    reg     [OUT_ADDR_WIDTH-1:0]            ACTION_stored_addr ;
    reg     [OUT_DATA_WIDTH-1:0]            ACTION_stored_data ;
    // path 1 signal
    wire                                    fifo_info_judge_convert ;
    assign fifo_info_judge_convert = ACTION_rd_addr[OUT_ADDR_WIDTH+1] ;

    // output signal
    reg                                     ACTION_rd_en_r          ;
    reg     [IN_DATA_WIDTH-1-32:0]          protocol_gen_data_r     ;
    reg                                     protocol_gen_sav_r      ;
    reg                                     protocol_gen_val_r      ;
    reg                                     protocol_gen_sop_r      ;
    reg                                     protocol_gen_eop_r      ;
    reg                                     convert_frame_info_r    ;
    
    // FIFO read enable delay
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            ACTION_rd_en_r_ff1 <= 1'b0 ;
        else
            ACTION_rd_en_r_ff1 <= ACTION_rd_en_r ;
    end

//**************************************************
// FSM
//**************************************************

    //----------------------------------------------
    // FSM declare
    //----------------------------------------------
    localparam  RESPONSE_IDLE               = 4'd0 ;
    localparam  RESPONSE_WAIT               = 4'd1 ;
    localparam  RESPONSE_JUDGE              = 4'd2 ;
    localparam  RESPONSE_CONVERT            = 4'd3 ;
    localparam  RESPONSE_SYNC_TRANS         = 4'd4 ;
    localparam  RESPONSE_TRANS              = 4'd5 ;
    localparam  RESPONSE_FETCH              = 4'd6 ;
    localparam  RESPONSE_JUDGE2             = 4'd7 ;
    localparam  RESPONSE_END                = 4'd8 ;

    reg     [3:0]                   response_state_c ;
    reg     [3:0]                   response_state_n ;

    // FSM(1)
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            response_state_c <= RESPONSE_IDLE ;
        else
            response_state_c <= response_state_n ;
    end
    // FSM(2)
    always @(*) begin
        case( response_state_c )
            RESPONSE_IDLE: begin
                if( protocol_gen_rdy ) begin
                    if( ACTION_has_store )
                        response_state_n = RESPONSE_CONVERT ;
                    else if( !ACTION_rd_empty_ff1 )
                        response_state_n = RESPONSE_WAIT ;
                    else
                        response_state_n = RESPONSE_IDLE ;
                end
                else
                    response_state_n = RESPONSE_IDLE ;
            end

            RESPONSE_WAIT: begin
                response_state_n = RESPONSE_JUDGE ;
            end

            RESPONSE_JUDGE: begin
                if( fifo_info_judge_convert )
                    response_state_n = RESPONSE_CONVERT ;
                else
                    response_state_n = RESPONSE_SYNC_TRANS ;
            end

            RESPONSE_CONVERT: begin
                response_state_n = RESPONSE_SYNC_TRANS ;
            end

            RESPONSE_SYNC_TRANS: begin
                if( protocol_start_trans )
                    response_state_n = RESPONSE_TRANS ;
                else
                    response_state_n = RESPONSE_SYNC_TRANS ;
            end

            RESPONSE_TRANS: begin
                if( !ACTION_FIFO_is_empty )
                    response_state_n = RESPONSE_FETCH ;
                else
                    response_state_n = RESPONSE_END ;
            end

            RESPONSE_FETCH: begin
                response_state_n = RESPONSE_JUDGE2 ;
            end

            RESPONSE_JUDGE2: begin
                if( fifo_info_judge_convert )
                    response_state_n = RESPONSE_END ;
                else
                    response_state_n = RESPONSE_TRANS ;
            end

            RESPONSE_END: begin
                response_state_n = RESPONSE_IDLE ;
            end

            default:
                response_state_n = RESPONSE_IDLE ;
        endcase
    end
    // FSM(3)
    // async FIFO read enable
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            ACTION_rd_en_r <= 1'b0 ;
        else if ( 
                    ((response_state_c == RESPONSE_IDLE) && (protocol_gen_rdy) && (!ACTION_has_store) && (!ACTION_rd_empty_ff1)) ||
                    ((response_state_c == RESPONSE_TRANS) && (!ACTION_FIFO_is_empty) )
                )
            ACTION_rd_en_r <= 1'b1 ;
        else
            ACTION_rd_en_r <= 1'b0 ;
    end
    // frame gen signal
    // "data"
    always @(posedge clk or negedge rst_n) begin
        if( !rst_n )
            protocol_gen_data_r <= {(IN_DATA_WIDTH-32){1'b0}} ;
        else if( response_state_c == RESPONSE_TRANS ) begin
                if( ACTION_has_store )
                    protocol_gen_data_r <= { ACTION_stored_order, ACTION_stored_wren, 1'b1, ACTION_stored_addr, ACTION_stored_data } ;
                else
                    protocol_gen_data_r <= { ACTION_rd_order_ff1, ACTION_rd_wren_ff1, 1'b1, ACTION_rd_addr_ff1, ACTION_rd_data_ff1 } ;
            end
        else
            protocol_gen_data_r <= protocol_gen_data_r ;
    end
    // "sav" signal
    always @(posedge clk or negedge rst_n) begin
        if( !rst_n )
            protocol_gen_sav_r <= 1'b0 ;
        else if(( response_state_c == RESPONSE_JUDGE )||( response_state_c == RESPONSE_CONVERT ))
            protocol_gen_sav_r <= 1'b1 ;
        else if( response_state_c == RESPONSE_END )
            protocol_gen_sav_r <= 1'b0 ;
        else
            protocol_gen_sav_r <= protocol_gen_sav_r ;
    end
    // "val" signal
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            protocol_gen_val_r <= 1'b0 ;
        else if( response_state_c == RESPONSE_TRANS )
            protocol_gen_val_r <= 1'b1 ;
        else
            protocol_gen_val_r <= 1'b0 ;
    end
    // "sop" signal
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            protocol_gen_sop_r <= 1'b0 ;
        else if( (response_state_c == RESPONSE_TRANS) && (TRANS_first) )
            protocol_gen_sop_r <= 1'b1 ;
        else
            protocol_gen_sop_r <= 1'b0 ;
    end
    // "eop" signal
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            protocol_gen_eop_r <= 1'b0 ;
        else if( ((response_state_c == RESPONSE_TRANS) && (ACTION_FIFO_is_empty)) || 
                    ((response_state_c == RESPONSE_JUDGE2) && (fifo_info_judge_convert)) )
            protocol_gen_eop_r <= 1'b1 ;
        else
            protocol_gen_eop_r <= 1'b0 ;
    end
    // "convert" signal
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            convert_frame_info_r <= 1'b0 ;
        else if( ((response_state_c == RESPONSE_IDLE) && (protocol_gen_rdy) && (ACTION_has_store)) ||
                    ((response_state_c == RESPONSE_JUDGE) && (fifo_info_judge_convert))  )
            convert_frame_info_r <= 1'b1 ;
        else
            convert_frame_info_r <= 1'b0 ;
    end


    //----------------------------------------------
    // internal signal define
    //----------------------------------------------
    // store flag
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) 
            ACTION_has_store <= 1'b0 ;
        else if( (response_state_c == RESPONSE_JUDGE2) && (fifo_info_judge_convert) )
            ACTION_has_store <= 1'b1 ;
        else if( response_state_c == RESPONSE_TRANS )
            ACTION_has_store <= 1'b0 ;
        else
            ACTION_has_store <= ACTION_has_store ;
    end
    // store content
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            ACTION_stored_order <= {(OUT_ORDER_WIDTH){1'b0}} ;
            ACTION_stored_wren <= {(1){1'b0}} ;
            ACTION_stored_addr <= {(OUT_ADDR_WIDTH){1'b0}} ;
            ACTION_stored_data <= {(OUT_DATA_WIDTH){1'b0}} ;
        end
        else if( (response_state_c == RESPONSE_JUDGE2) && (fifo_info_judge_convert) ) begin
            ACTION_stored_order <= ACTION_rd_order ;
            ACTION_stored_wren <= ACTION_rd_addr[OUT_ADDR_WIDTH] ;
            ACTION_stored_addr <= ACTION_rd_addr[OUT_ADDR_WIDTH-1:0] ;
            ACTION_stored_data <= ACTION_rd_data ;
        end
        else begin
            ACTION_stored_order <= ACTION_stored_order ;
            ACTION_stored_wren <= ACTION_stored_wren ;
            ACTION_stored_addr <= ACTION_stored_addr ;
            ACTION_stored_data <= ACTION_stored_data ;
        end
    end

    // trans first time
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            TRANS_first <= 1'b1 ;
        else if( response_state_c == RESPONSE_IDLE )
            TRANS_first <= 1'b1 ;
        else if( response_state_c == RESPONSE_TRANS )
            TRANS_first <= 1'b0 ;
        else
            TRANS_first <= TRANS_first ;
    end


//**************************************************
// output assign
//**************************************************
    assign ACTION_rd_en         = ACTION_rd_en_r        ;
    assign protocol_gen_data    = protocol_gen_data_r   ;
    assign protocol_gen_sav     = protocol_gen_sav_r    ;
    assign protocol_gen_val     = protocol_gen_val_r    ;
    assign protocol_gen_sop     = protocol_gen_sop_r    ;
    assign protocol_gen_eop     = protocol_gen_eop_r    ;
    assign convert_frame_info   = convert_frame_info_r  ;
endmodule
